package util;

/**
 * @author CV大魔王
 * @Description
 * @date 2021/3/14 20:26
 */

import java.io.Serializable;

/**
 * 注：为方便理解，所有并发判断的处理，均未实现
 * @author CV大魔王
 * @Description ArrayList简单实现版
 * @date 2021/3/14 10:18
 */
public class MyArrayList implements Serializable {

    // 第一次扩容的容量
    public static final int DEFAULT_CAPACITY = 10;

    // 用于初始化空的list
    public static final Object[] EMPTY_ELEMENT_DATA = {};

    // 实际存储的元素 注：transient含义是不让此元素被序列化
    transient Object[] elementData;

    // 实际list集合大小，从0开始
    private int size;

    // 使用这个字段，来判断当前集合类是否被并发修改，即迭代器并发修改的fail-fast机制
    // private transient int modCount = 0;

    /**
     * 空参构造函数，将空对象赋值给elementData，elementData是用来实际存储的元素
     */
    public MyArrayList() {
        this.elementData = EMPTY_ELEMENT_DATA;
    }

    /**
     * 有参构造函数
     *
     * @param initialCapacity 初始容量
     */
    public MyArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENT_DATA;
        } else {
            // 如果参数不合规范就抛出异常IllegalArgumentException表示参数不合法。
            throw new IllegalArgumentException("参数异常");
        }
    }


    /**
     * 向数组添加内容
     * 注：需要注意这里是怎样进行扩容的
     *
     * @param e 添加的内容
     * @return 逻辑值，表示插入是否成功
     */
    public boolean add(Object e) {
        // 判断容量
        ensureCapacityInternal(size + 1);
        // 使用下标复制，尾部插入
        elementData[size++] = e;

        return true;
    }


    /**
     * 计算容量+确保容量
     *
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity) {
        // 如果是初次扩容，则使用默认容量
        if (elementData == EMPTY_ELEMENT_DATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        // 是否需要扩容，需要的最少容量大于现在数组长度则要扩容
        if (minCapacity - elementData.length > 0) {
            // 获取到旧容量
            int oldCapacity = elementData.length;
            // 动态扩容机制：旧容量 + 旧容量/2
            int newCapacity = oldCapacity + (oldCapacity >> 1);

            // 如果新容量 < 最小容量，则将最新容量赋值给新的容量
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }

            // 创建新数组
            Object[] objects = new Object[newCapacity];

            //将旧的数组复制到新的数组里面
            System.arraycopy(elementData, 0, objects, 0, elementData.length);

            // 修改引用
            elementData = objects;
        }
    }


    /**
     * 通过下标获取对象
     * @param index 索引(用户通过索引获取对象)
     * @return 返回对象
     */
    public Object get(int index) {
        rangeCheck(index);
        return elementData[index];
    }

    /**
     * 检查索引是否越界
     * @param index 索引
     */
    private void rangeCheck(int index) {
        if (index > size || size < 0) {
            throw new IndexOutOfBoundsException("数组越界");
        }
    }

    /**
     * 判断对象所在位置
     * @param o 对象内容
     * @return 对应位置索引
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++) {
                if (elementData[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = 0; i < size; i++) {
                if (o.equals(elementData[i])) {
                    return i;
                }
            }
        }
        return -1;
    }


    /**
     * 更新
     * @param index 索引位置
     * @param obj 更新后的值
     * @return 旧的值
     */
    public Object set(int index,Object obj){
        // 检查合法性
        rangeCheck(index);
        // 获取旧的值
        Object oldValue = elementData[index];
        // 将新的值赋值
        elementData[index] = obj;
        return oldValue;
    }


    /**
     * 根据索引删除元素
     * 难点分析：元素移动
     * @param index 索引
     * @return 删除的元素的值
     */
    public Object remove(int index){
        // 校验合法性
        rangeCheck(index);
        // 获取旧的内容
        Object oldVale = elementData[index];
        // 计算要删除的位置后面有几个元素
        int numMoved = size - index - 1;

        if (numMoved > 0) {
            // 1.原数组 2.原数据的起始位置 3.目标数组 4.目标数组的开始起始位置 5.要copy的数组的长度
            System.arraycopy(elementData,index+1,elementData,index,numMoved);
        }
        // 将多出的位置置空，补充：置空后的对象，jvm垃圾回收机制，会自动进行回收。如果不进行置空，引用还在，可能会出现内存泄漏
        elementData[--size] = null;
        return oldVale;
    }

    /**
     * 获取集合大小
     * @return 集合大小
     */
    public int size(){
        return size;
    }

}
